let _ = require('lodash');
const Joi = require('@hapi/joi');
let sequelize = require('../libs/db.lib');
let userModel = require('../models/user.model');
let videoModel = require('../models/video.model');
let moment = require('moment');
let md5 = require('blueimp-md5');
let config = require('../config');
const mailer = require('nodemailer');
let jwt = require('jsonwebtoken');
let Op = require('sequelize').Op;

/**
 * 生成验证码
 * @returns {string}
 */
function genVerifyCode() {
    let verifyCode = '';
    for (let i = 0; i < 6; i++) {
        verifyCode += Math.floor(Math.random() * 10);
    }
    return verifyCode;
}

/**
 * 发送验证邮件
 * @param address
 * @param code
 */
function sendVerifyMail(address, code) {
    const mail = {
        from: config.mail.from.replace('$appName', config.system.appName),
        subject: '注册验证 - ' + config.system.appName,//邮箱主题
        to: address,
        html: '您的验证码为  <strong>' + code + '</strong>',//发送验证码
    };
    const transporter = mailer.createTransport(config.mail.smtp);
    return transporter.sendMail(mail);
}

function currentUid(req) {
    let token = req.body.token;
    let decoded = jwt.decode(token, config.tokenSecret);
    return decoded.id;
}

module.exports = {
    /**
     * 权限拦截
     * @param req
     * @param res
     * @param next
     */
    auth(req, res, next) {
        let reqUrl = req.originalUrl.split("?").shift();
        switch (reqUrl) {
            case '/':
            case '/info':
            case '/user/logout':
            case '/user/login':
            case '/user/reg':
            case '/user/auth':
                next();
                break;
            default: {
                let token = null;
                if (req.method.toUpperCase() === 'GET' && req.query && req.query.token) {
                    token = req.query.token;
                }
                if (req.method.toUpperCase() === 'POST' && req.body && req.body.token) {
                    token = req.body.token;
                }
                if (!token) {
                    res.httpError(401);
                    return;
                } else {
                    jwt.verify(token, config.tokenSecret, function (err, decoded) {
                        if (err || !decoded) {
                            res.httpError(401);
                            return;
                        }
                        if (decoded.ip !== req.ip) {
                            if (decoded.email !== 'demo@x.cn') {
                                res.httpError(401);
                                return;
                            }
                        }
                        userModel.findOne({attributes: ['id', 'email', 'token', 'role', 'block'], where: {id: decoded.id}}).then(u => {
                            if (!u) {
                                res.httpError(401);
                                return null;
                            }
                            if (u.block) {
                                res.httpError(401);
                                return null;
                            }
                            if (u.email !== decoded.email) {
                                res.httpError(401);
                                return null;
                            }
                            if (_.startsWith(reqUrl, '/admin/') && u.role !== 0) {
                                res.httpError(401);
                                return null;
                            }
                            if (token === u.token || u.email === 'demo@x.cn') {
                                next();
                                return null;
                            } else {
                                res.httpError(401);
                            }
                        }).catch(e => {
                            res.httpError(401);
                        });
                    });
                }
                break;
            }
        }
    },

    /**
     * 获取用户信息
     * @param req
     * @param res
     * @param next
     */
    info(req, res, next) {
        userModel.findOne({attributes: ['email', 'nickname', 'mobile', 'role'], where: {id: currentUid(req)}}).then(u => {
            if (!u) {
                res.jsonFail('用户不存在！');
            }
            res.jsonSuccess(u);
        }).catch(e => {
            res.jsonFail('用户信息读取失败！');
        });
    },

    /**
     * 获取历史记录
     * @param req
     * @param res
     * @param next
     */
    history(req, res, next) {
        let uid = currentUid(req);
        userModel.findOne({attributes: ['history'], where: {id: uid}}).then(u => {
            if (!u) {
                res.jsonFail('用户不存在!');
            }
            let vids = [];
            if (u && u.history) {
                for (let idx in u.history) {
                    vids.push(u.history[idx].id);
                }
            }
            if (vids.length !== 0) {
                return videoModel.findAll({
                    attributes: ['id', 'name', 'pic'],
                    where: {
                        id: {
                            [Op.in]: vids,
                        },
                    },
                    order: sequelize.literal("field(id," + vids.join(',') + ")"),
                }).then(vs => {
                    res.jsonSuccess(vs);
                }).catch(e => {
                    res.jsonFail(e.message);
                });
            } else {
                res.jsonSuccess();
            }
        }).catch(e => {
            res.jsonFail(e.message);
        });
    },

    /**
     * 删除历史记录
     * @param req
     * @param res
     * @param next
     */
    historyDel(req, res, next) {
        let uid = currentUid(req);
        let id = req.body.id;
        if (_.isNil(id)) {
            res.jsonFail('未知ID');
            return;
        }
        return userModel.findOne({attributes: ['id', 'history'], where: {id: uid}}).then(u => {
            if (u && u.history) {
                let history = u.history;
                for (let idx in history) {
                    if (history[idx].id === id) {
                        history.splice(idx, 1);
                        break;
                    }
                }
                u.history = history;
                return u.save().then(() => {
                    res.jsonSuccess();
                }).catch(e => {
                    res.jsonFail(e.message);
                });
            }
        }).catch(e => {
            res.jsonFail(e.message);
        });
    },

    /**
     * 更新用户信息
     * @param req
     * @param res
     * @param next
     */
    update(req, res, next) {
        let token = req.body.token;
        let email = req.body.email;
        let pwd = req.body.pass;
        let nickname = req.body.nickname;
        let mobile = req.body.mobile;

        if (!email || !nickname || !mobile) {
            res.jsonFail('邮箱,密码,昵称,手机号不能为空！');
            return;
        }
        if (Joi.string().email().validate(email).error) {
            res.jsonFail('邮箱格式错误！');
            return;
        }
        if (Joi.string().min(2).max(20).validate(nickname).error) {
            res.jsonFail('昵称长度2~10！');
            return;
        }
        if (pwd && Joi.string().regex(/^[a-zA-Z0-9]{6,16}$/).validate(pwd).error) {
            res.jsonFail('密码只能是字母和数字并且长度6~16！');
            return;
        }
        if (Joi.string().regex(/^1[3|4|5|6|7|8|9][0-9]{9}$/).validate(mobile).error) {
            res.jsonFail('手机号格式错误！');
            return;
        }
        userModel.findOne({attributes: ['id', 'email'], where: {id: currentUid(req)}}).then(u => {
            if (!u || u.email !== email) {
                res.jsonFail('用户不存在！');
            }
            let logout = 0;
            if (pwd) {
                u.password = md5(pwd);
                u.token = '';
                logout = 1;
            }
            u.nickname = nickname;
            u.mobile = mobile;
            return u.save().then(
                u => {
                    res.jsonSuccess({logout: logout});
                },
            ).catch(e => {
                    res.jsonFail(`系统错误！`);
                },
            );
        }).catch(e => {
            res.jsonFail('用户信息读取失败！');
        });
    },

    /**
     * 新用户注册
     * @param req
     * @param res
     * @param next
     */
    reg(req, res, next) {
        if (!config.system.appOpenReg) {
            res.jsonFail('暂未开放注册！');
            return;
        }
        let email = req.body.email;
        let pwd = req.body.pass;
        let nickname = req.body.nickname;
        let mobile = req.body.mobile;

        if (!email || !pwd || !nickname || !mobile) {
            res.jsonFail('邮箱,密码,昵称,手机号不能为空！');
            return;
        }
        if (Joi.string().email().validate(email).error) {
            res.jsonFail('邮箱格式错误！');
            return;
        }
        if (Joi.string().min(2).max(20).validate(nickname).error) {
            res.jsonFail('昵称长度2~10！');
            return;
        }
        if (Joi.string().regex(/^[a-zA-Z0-9]{6,16}$/).validate(pwd).error) {
            res.jsonFail('密码只能是字母和数字并且长度6~16！');
            return;
        }
        if (Joi.string().regex(/^1[3|4|5|6|7|8|9][0-9]{9}$/).validate(mobile).error) {
            res.jsonFail('手机号格式错误！');
            return;
        }

        userModel.findOne({attributes: ['id'], where: {email: email}}).then(u => {
            if (u) {
                res.jsonFail(`Email ${email} 已被注册！`);
            } else {
                let code = genVerifyCode();
                return userModel.create({email: email, password: md5(pwd), nickname: nickname, mobile: mobile, verified: false, codeGenAt: moment().valueOf(), verifyCode: code, role: 1, createdAt: moment().valueOf()}).then(u => {
                    return sendVerifyMail(email, code).then(mail => {
                        res.jsonSuccess({id: u.id}, '注册成功！');
                    }).catch(e => {
                        res.jsonSuccess({id: u.id}, '注册成功，验证码发送失败！');
                    });
                }).catch(e => {
                    res.jsonFail(e.message);
                });
            }
        }).catch(e => {
            res.jsonFail(e.message);
        });
    },

    /**
     * 用户登录
     * @param req
     * @param res
     * @param next
     */
    login(req, res, next) {
        let email = req.body.email;
        let pwd = req.body.pass;
        let verifyCode = req.body.verifyCode;

        if (!email || !pwd) {
            res.jsonFail('邮箱,密码不能为空！');
            return;
        }
        if (Joi.string().email().validate(email).error) {
            res.jsonFail('邮箱格式错误！');
            return;
        }
        if (verifyCode && Joi.string().length(6).validate(verifyCode).error) {
            res.jsonFail('验证码长度为6！');
            return;
        }
        if (Joi.string().regex(/^[a-zA-Z0-9]{6,16}$/).validate(pwd).error) {
            res.jsonFail('密码只能是字母和数字并且长度6~16！');
            return;
        }

        userModel.findOne({attributes: ['id', 'password', 'nickname', 'verified', 'verifyCode', 'role', 'block', 'token', 'history'], where: {email: email}}).then(u => {
            if (!u) {
                res.jsonFail(`用户不存在！`);
                return;
            }
            if (u.block) {
                res.jsonFail(`账号被禁用！`);
                return;
            }
            if (u.password !== md5(pwd)) {
                res.jsonFail(`密码错误！`);
                return;
            }
            if (!u.verified && !verifyCode) {
                res.jsonSuccess({verified: false});
                return;
            }
            if (verifyCode && u.verifyCode !== verifyCode) {
                res.jsonFail(`验证码错误！`);
                return;
            }
            // 生成token 存储并返回给客户端
            let token = jwt.sign({id: u.id, ip: req.ip, email: email}, config.tokenSecret, {
                expiresIn: 60 * 60 * 48,  // 48小时过期
            });
            u.verified = true;
            u.token = token;
            return u.save().then(
                u => {
                    res.jsonSuccess({verified: true, token: token, user: u.nickname, role: u.role});
                },
            ).catch(e => {
                    res.jsonFail(`系统错误！`);
                },
            );
        }).catch(e => {
            res.jsonFail(e.message);
        });
    },

    /**
     * 重发验证码
     * @param req
     * @param res
     * @param next
     */
    resendCode(req, res, next) {
        let email = req.body.email;
        let pwd = req.body.pass;

        if (!email || !pwd) {
            res.jsonFail('邮箱,密码不能为空！');
            return;
        }
        if (Joi.string().email().validate(email).error) {
            res.jsonFail('邮箱格式错误！');
            return;
        }
        if (Joi.string().regex(/^[a-zA-Z0-9]{6,16}$/).validate(pwd).error) {
            res.jsonFail('密码只能是字母和数字并且长度6~16！');
            return;
        }

        userModel.findOne({attributes: ['id', 'password', 'codeGenAt'], where: {email: email}}).then(u => {
            if (!u) {
                res.jsonFail(`用户不存在！`);
                return;
            }
            if (u.password !== md5(pwd)) {
                res.jsonFail(`密码错误！`);
                return;
            }

            // 一分钟内只允许发一次验证码
            let codeCD = 60000 - (moment().valueOf() - moment(u.codeGenAt).valueOf());
            if (codeCD > 0) {
                res.jsonFail(`验证码发送频繁，请${_.toInteger(codeCD / 1000)}秒后再试！`);
                return;
            }
            let code = genVerifyCode();
            u.verifyCode = code;
            u.codeGenAt = moment().valueOf();
            return u.save().then(
                u => {
                    return sendVerifyMail(email, code).then(mail => {
                        res.jsonSuccess(null);
                    }).catch(e => {
                        res.jsonFail('验证码发送失败！');
                    });
                },
            ).catch(e => {
                    res.jsonFail(`系统错误！`);
                },
            );
        }).catch(e => {
            res.jsonFail(e.message);
        });
    },

    /**
     * 获取用户列表
     * @param req
     * @param res
     * @param next
     */
    users(req, res, next) {
        let page = req.body.page;
        let pageSize = req.body.pageSize;
        if (_.isNil(page)) {
            page = 1;
        }
        if (_.isNil(pageSize)) {
            pageSize = 20;
        }
        userModel.findAndCountAll({
            attributes: ['id', 'nickname', 'email', 'role', 'block'],
            where: {
                id: {
                    [Op.ne]: currentUid(req),
                },
            },
            limit: pageSize,
            offset: pageSize * (page - 1),
        }).then(us => {
            res.jsonSuccess(us);
        }).catch(e => {
            res.jsonFail(e.message);
        });
    },

    /**
     * 更新用户角色
     * @param req
     * @param res
     * @param next
     */
    userRole(req, res, next) {
        let uid = req.body.uid;
        let role = req.body.role;

        if (_.isNil(role)) {
            res.jsonFail('角色错误！');
            return;
        }
        role = _.toInteger(role);
        if (role !== 0 && role !== 1) {
            res.jsonFail('角色错误！');
            return;
        }
        userModel.findOne({attributes: ['id'], where: {id: uid}}).then(u => {
            if (!u) {
                res.jsonFail('用户不存在！');
            }
            u.role = role;
            return u.save().then(
                u => {
                    res.jsonSuccess();
                },
            ).catch(e => {
                    res.jsonFail(`系统错误！`);
                },
            );
        }).catch(e => {
            res.jsonFail('用户信息读取失败！');
        });
    },

    /**
     * 启用 禁用 用户
     * @param req
     * @param res
     * @param next
     */
    userBlock(req, res, next) {
        let uid = req.body.uid;
        userModel.findOne({attributes: ['id', 'block'], where: {id: uid}}).then(u => {
            if (!u) {
                res.jsonFail('用户不存在！');
            }
            u.block = !u.block;
            return u.save().then(
                u => {
                    res.jsonSuccess();
                },
            ).catch(e => {
                    res.jsonFail(`系统错误！`);
                },
            );
        }).catch(e => {
            res.jsonFail('用户信息读取失败！');
        });
    },

    /**
     * 删除 用户
     * @param req
     * @param res
     * @param next
     */
    userDel(req, res, next) {
        let uid = req.body.uid;
        userModel.findOne({attributes: ['id'], where: {id: uid}}).then(u => {
            if (!u) {
                res.jsonFail('用户不存在！');
            }
            return u.destroy().then(
                u => {
                    res.jsonSuccess();
                },
            ).catch(e => {
                    res.jsonFail(`系统错误！`);
                },
            );
        }).catch(e => {
            res.jsonFail('用户信息读取失败！');
        });
    },
};
